package com.zcyh.exam.job.quartz.config.quartz;


import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zcyh.exam.job.CronUtils;
import com.zcyh.exam.mapper.QuartzMapper;
import com.zcyh.exam.model.entity.QuartzInfo;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 自定义任务调度
 * 采用原生Quartz替代SpringQuartz
 * 暂不支持分组功能
 * @author
 * @version 1.0
 */
@Service
public class QuartzTaskScheduler{
	@Resource(name = "scheduler")
    private Scheduler taskScheduler;
	@Autowired
    private QuartzMapper quartzMapper;

    private Map<String, Trigger> triggerMap = new HashMap<String, Trigger>();
    private Map<String, JobDetail> jobMap = new HashMap<String, JobDetail>();

    private String jobSuf = "_job";

    /***********************
     *   SimpleJob         *
     ***********************/
    /**
     * 开始一个简单任务
     *
     * @param jobTag         任务标记 CAUTION: 必须唯一
     * @param repeatInternal 循环间隔时间 单位：秒,-1为只执行一次
     * @param concurrent 是否并发 true-并发，false-不并发
     * @param args  传入的参数
     *
     * @return 生成的id号
     */
    @SuppressWarnings({ "unchecked", "rawtypes", "serial", "finally" })
	public String startSimpleJob(String jobTag, int repeatInternal, boolean concurrent, Map<String, String> args){

        final String jobId = jobTag + jobSuf + getIdNum();
        try{
            if(null == args){
            	args = new HashMap(){{
                    put("jobId", jobId);
                }};
            }else{
            	args.put("jobId", jobId);
            }
            JobDataMap dataMap = new JobDataMap();
            dataMap.put("serviceTag", jobTag);
            if(null != args){
            	dataMap.put("args", args);
            }
            Class<? extends Job> excuteClass = concurrent ? QuartzJobBean.class : QuartzStatefulJobBean.class;
            JobDetail jobDetail = JobBuilder.newJob(excuteClass).setJobData(dataMap).storeDurably(true).withIdentity(jobId, Scheduler.DEFAULT_GROUP).build();
            SimpleScheduleBuilder builder;
            if(repeatInternal == -1){
            	builder = SimpleScheduleBuilder.simpleSchedule();
            }else{
            	builder = SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(repeatInternal);
            }
            SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger().withIdentity(jobId, Scheduler.DEFAULT_GROUP).withSchedule(builder).forJob(jobId, Scheduler.DEFAULT_GROUP).build();
            taskScheduler.scheduleJob(jobDetail, simpleTrigger);
            jobMap.put(jobId, jobDetail);
            triggerMap.put(jobId, simpleTrigger);
        } catch (SchedulerException se){
            se.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        } finally{
            return jobId;
        }
    }

    /**
     * 重新恢复调度并更新repeat时间
     *
     * @param jobId
     * @param newInternal
     * @return 生成的id号
     *
     */
    @SuppressWarnings("finally")
	protected String rescheduleSimpleJob(String jobId, int newInternal){
        try{
            SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger().withIdentity(jobId, Scheduler.DEFAULT_GROUP).withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(newInternal)).forJob(jobId, Scheduler.DEFAULT_GROUP).build();
            Trigger old = triggerMap.get(jobId);
            taskScheduler.rescheduleJob(old.getKey(), simpleTrigger);
        } catch (SchedulerException se){
            se.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        } finally{
            return jobId;
        }
    }

    /**
     * 为一个job分配一个新的Trigger
     *
     * @param jobId
     * @param newInternal   循环间隔时间 单位：秒,-1为只执行一次
     * @param args  传入的参数
     *
     * @return 生成的id号
     */
    @SuppressWarnings({ "rawtypes", "unchecked", "serial", "finally" })
	protected String startSimpleTriggerForJob(final String jobId, int newInternal, Map<String, String> args){
        try{
            if(null == args){
            	 args = new HashMap(){{
                     put("jobId", jobId);
                 }};
            }else{
            	 args.put("jobId", jobId);
            }
            SimpleScheduleBuilder builder;
            if(newInternal == -1){
            	builder = SimpleScheduleBuilder.simpleSchedule();
            }else{
            	builder = SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(newInternal);
            }
            SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger().withIdentity(jobId, Scheduler.DEFAULT_GROUP).withSchedule(builder).forJob(jobId, Scheduler.DEFAULT_GROUP).build();
            JobDetail job = jobMap.get(jobId);
            if(null != args){
                job.getJobDataMap().put("args", args);
            }
            taskScheduler.deleteJob(job.getKey());
            taskScheduler.scheduleJob(job, simpleTrigger);
            triggerMap.put(jobId, simpleTrigger);
        }catch (SchedulerException se){
            se.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        } finally{
            return jobId;
        }
    }

    /**
     * 刷新一个服务，赋予新的循环时间
     *
     * @param jobId
     * @param newInternal
     *
     * @return 生成的id号
     */
    public String refreshSimpleJob(String jobId, int newInternal, Map<String, String> args){
        unscheduleJob(jobId);
        return startSimpleTriggerForJob(jobId, newInternal, args);
    }



    /***********************
     *       CornJob       *
     ***********************/
    public String startCronJobDate(String jobTag, Date cron,  Map<String, String> args,String execMsg){
        QuartzInfo quartzInfo = new QuartzInfo(jobTag, cron, "0", execMsg);
        quartzMapper.insert(quartzInfo);
        args.put("uuid",quartzInfo.getId());
        String s = startCronJob(jobTag, CronUtils.getCron(cron), true, args);

        quartzMapper.update(null, Wrappers.<QuartzInfo>lambdaUpdate()
                .set(QuartzInfo::getJobId,s)
                .eq(QuartzInfo::getId,quartzInfo.getId())
        );
        return s;
    }
    /**
     * 开始一个cron任务
     *
     * @param jobTag serviceTag
     * @param cron   corn表达式
     * @param concurrent 是否并发 true-并发，false-不并发
     * @param args  传入的参数
     *
     * @return 生成的id号
     */
    @SuppressWarnings({ "rawtypes", "unchecked", "serial", "finally" })
	public String startCronJob(String jobTag, String cron, boolean concurrent, Map<String, String> args){
        final String jobId = jobTag + jobSuf + getIdNum();
        try{
            if(null == args){
            	args = new HashMap(){{
                    put("jobId", jobId);
                }};
            }else{
            	args.put("jobId", jobId);
            }
            JobDataMap dataMap = new JobDataMap();
            dataMap.put("serviceTag", jobTag);
            if(null != args){
            	dataMap.put("args", args);
            }
            Class<? extends Job> excuteClass = concurrent ? QuartzJobBean.class : QuartzStatefulJobBean.class;
            JobDetail jobDetail = JobBuilder.newJob(excuteClass).setJobData(dataMap).storeDurably(true).withIdentity(jobId, Scheduler.DEFAULT_GROUP).build();
            CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobId, Scheduler.DEFAULT_GROUP).withSchedule(CronScheduleBuilder.cronSchedule(cron)).forJob(jobId, Scheduler.DEFAULT_GROUP).build();
            taskScheduler.scheduleJob(jobDetail, cronTrigger);
            jobMap.put(jobId, jobDetail);
            triggerMap.put(jobId, cronTrigger);
        } catch (SchedulerException se){
            se.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        } finally{
            return jobId;
        }
    }

    /**
     * 为一个Cron job分配一个新的Trigger
     *
     * @param jobId
     * @param newCron
     * @param args  传入的参数
     *
     * @return 生成的id号
     */
    @SuppressWarnings({ "unchecked", "rawtypes", "serial", "finally" })
	protected String startCronTriggerForJob(final String jobId, String newCron, Map<String, String> args){
        try{
            if(null == args){
            	args = new HashMap(){{
                    put("jobId", jobId);
                }};
            }
            else{
            	args.put("jobId", jobId);
            }
            JobDetail job = jobMap.get(jobId);
            if(null != args){
                job.getJobDataMap().put("args", args);
            }
            CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobId, Scheduler.DEFAULT_GROUP).withSchedule(CronScheduleBuilder.cronSchedule(newCron)).forJob(jobId, Scheduler.DEFAULT_GROUP).build();
            taskScheduler.deleteJob(job.getKey());
            taskScheduler.scheduleJob(job, cronTrigger);
            triggerMap.put(jobId, cronTrigger);
        } catch (SchedulerException e){
            e.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        } finally{
            return jobId;
        }

    }

    /**
     * 刷新一个服务，赋予新的循环时间
     *
     * @param jobId
     * @param newCron
     */
    public String refreshCronJob(String jobId, String newCron, Map<String, String> args){
        unscheduleJob(jobId);
        return startCronTriggerForJob(jobId, newCron, args);
    }

    /**
     * 删除一个job
     * @param jobId
     */
    public void terminateJob(String jobId){
        try{
            Trigger trigger = triggerMap.get(jobId);
            taskScheduler.unscheduleJob(trigger.getKey());
            JobDetail job = jobMap.get(jobId);
            taskScheduler.deleteJob(job.getKey());
            triggerMap.remove(jobId);
        } catch (SchedulerException se){
            se.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 结束一个trigger
     *
     * @param jobId
     */
    protected void unscheduleJob(String jobId){
        try{
            Trigger trigger = triggerMap.get(jobId);
            taskScheduler.unscheduleJob(trigger.getKey());
            triggerMap.remove(jobId);
        } catch (SchedulerException se){
            se.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 删除job
     *
     * @param jobId
     */
    protected void deleteJob(String jobId){
        try{
            JobDetail job = jobMap.get(jobId);
            taskScheduler.deleteJob(job.getKey());
            jobMap.remove(jobId);
        } catch (SchedulerException e){
            e.printStackTrace();
        }
    }

    private String getIdNum(){
        return "_" + StrUtil.uuid();
    }
}
